package com.tysong.qq.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.apache.commons.io.IOUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
public class HttpClientUtil {
    private static RequestConfig requestConfig;
    private static PoolingHttpClientConnectionManager cm;
    private static CloseableHttpClient httpClient = null;
    private static String EMPTY_STR = "";
    private static String charSet = "UTF-8";
    private static HttpClient client = null;

    /**初始化httpClient**/
    static {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(128);
        cm.setDefaultMaxPerRoute(128);
        client = HttpClients.custom().setConnectionManager(cm).build();
    }

    private static void init() {
        if (requestConfig == null) {
            synchronized (RequestConfig.class) {
                if (requestConfig == null) {
                    requestConfig = RequestConfig.custom()
                            .setSocketTimeout(3000)
                            .setConnectTimeout(3000)
                            .setRedirectsEnabled(false)
                            .build();
                }
            }
        }

        if (cm == null) {
            synchronized (PoolingHttpClientConnectionManager.class) {
                if (cm == null) {
                    cm = new PoolingHttpClientConnectionManager();
                    cm.setMaxTotal(1500);
                    cm.setDefaultMaxPerRoute(150);
                    cm.closeExpiredConnections();
                    cm.closeIdleConnections(2000, TimeUnit.MILLISECONDS);
                }
            }
        }
    }


    private static CloseableHttpClient getHttpClient() {
        init();
        if (httpClient == null) {
            synchronized (CloseableHttpClient.class) {
                if (httpClient == null) {
                    httpClient = HttpClients.custom()
                            .setConnectionManager(cm)
                            .setDefaultRequestConfig(requestConfig)
                            .build();
                }
            }
        }
        return httpClient;
    }


    public static String httpGetRequest(String url) {
        HttpGet httpGet = new HttpGet(url);
        return getResult(httpGet);
    }

    public static class SSLClient extends DefaultHttpClient {
        public SSLClient() throws Exception {
            super();
            SSLContext ctx = SSLContext.getInstance("TLS");
            X509TrustManager tm = new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain,
                                               String authType) throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain,
                                               String authType) throws CertificateException {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
            ctx.init(null, new TrustManager[]{tm}, null);
            SSLSocketFactory ssf = new SSLSocketFactory(ctx);
            ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            ClientConnectionManager ccm = this.getConnectionManager();
            SchemeRegistry sr = ccm.getSchemeRegistry();
            sr.register(new Scheme("https", ssf, 443));
        }
    }

    public static String httpPostBody(String url, String json) throws Exception {
        CloseableHttpClient httpClient = new SSLClient();
        try {
            HttpPost httpPost = new HttpPost(url);
            StringEntity stringEntity = new StringEntity(json, Charset.forName("UTF-8"));
            stringEntity.setContentEncoding("UTF-8");
            httpPost.setEntity(stringEntity);
            httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
            httpPost.setHeader("Accept", "application/json");
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            return httpClient.execute(httpPost, responseHandler);
            //return getResult(httpPost);
        } catch (Exception e) {
            throw new Exception(e);
        }
    }


    private static String getResult(HttpRequestBase request) {
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(request);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String result = EntityUtils.toString(entity, "utf-8");
                EntityUtils.consume(entity);
                response.close();
                return result;
            }
        } catch (Exception e) {
            request.abort();
        } finally {
            request.releaseConnection();
            if (null != response) {
                try {
                    response.close();
                } catch (IOException e) {
                }
            }
        }
        return EMPTY_STR;
    }

    public static String get(String url, Map<String, String> headers, Integer connTimeOut, Integer readTimeOut)
            throws Exception {
        return HttpClientUtil.get(url, "utf-8", headers, null, connTimeOut, readTimeOut);
    }

    /**
     * 发送一个 GET 请求
     *
     * @param url         请求地址
     * @param charSet     编码格式
     * @param headers     请求头信息
     * @param token       token
     * @param connTimeOut 链接超时时间（毫秒）
     * @param readTimeOut 响应超时时间（毫秒）
     * @return
     * @throws Exception
     */
    public static String get(String url, String charSet, Map<String, String> headers, String token, Integer connTimeOut,
                             Integer readTimeOut) throws Exception {
        log.info("发送HTTP(S) GET 请求：" + url + " | " + charSet + " | " + connTimeOut + " | " + readTimeOut);
        HttpClient client = null;
        HttpResponse res = null;
        HttpGet get = new HttpGet(url);
        String result = "";
        try {
            // 设置参数
            RequestConfig.Builder customReqConf = RequestConfig.custom();
            if (connTimeOut != null) {
                customReqConf.setConnectTimeout(connTimeOut);
            }
            if (readTimeOut != null) {
                customReqConf.setSocketTimeout(readTimeOut);
            }
            get.setConfig(customReqConf.build());

            if (headers != null && !headers.isEmpty()) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    get.addHeader(entry.getKey(), entry.getValue());
                }
            }
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(get);
            } else {
                // 执行 Http 请求.
                client = HttpClientUtil.client;
                res = client.execute(get);
            }
            result = getResult(res, url);
        } catch (Exception e) {
            if (res != null) {
                log.error("HTTP(S) GET 请求，状态异常：" + res.getStatusLine().getStatusCode() + " | " + url);
                throw e;
            }
        } finally {
            get.releaseConnection();
            if (null != res) {
                EntityUtils.consumeQuietly(res.getEntity());
            }
            if (url.startsWith("https") && client != null
                    && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }

    /**
     * 用于解决javax.net.ssl.SSLException
     *
     * @return
     * @throws GeneralSecurityException
     */
    private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
        RequestConfig.Builder requestBuilder = RequestConfig.custom();
        requestBuilder = requestBuilder.setSocketTimeout(10000);
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, new TrustManager[]{new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            }
            }, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            log.error("createSSLInsecureClient发生异常", e);
        } catch (KeyManagementException e) {
            log.error("createSSLInsecureClient发生异常", e);
        }
        CloseableHttpClient client = HttpClientBuilder.create().setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext)).setDefaultRequestConfig(requestBuilder.build()).setSSLHostnameVerifier(new DefaultHostnameVerifier()).build();
        return client;
    }

    /**
     * 封装接口调用返回结果
     *
     * @param res 响应信息
     * @param url 地址
     * @return
     * @throws IOException
     */
    private static String getResult(HttpResponse res, String url) throws IOException {
        String result = "";
        if (res.getStatusLine().getStatusCode() == 200) {
            result = IOUtils.toString(res.getEntity().getContent(), charSet);
        } else {
            log.info("HTTP(S) 请求，状态异常：" + res.getStatusLine().getStatusCode() + " | " + url);
            throw new IOException("HTTP(S) 请求，状态异常：" + res.getStatusLine().getStatusCode() + " | " + url);
        }
        return result;
    }
}
